package server;

import cn.hutool.core.util.StrUtil;
import javafx.util.converter.ShortStringConverter;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import server.bean.Context;
import server.bean.Host;
import server.bean.Mapper;
import server.bean.Wrapper;
import server.config.WebConfig;
import server.utils.MyClassLoader;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

/**
 * Minicat的主类
 */
public class Bootstrap {

    /**
     * 定义socket监听的端口号
     */
    private int port = 8080;

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    private Mapper mapper = new Mapper();

    public Mapper getMapper() {
        return mapper;
    }

    public void setMapper(Mapper mapper) {
        this.mapper = mapper;
    }

    /**
     * Minicat启动需要初始化展开的一些操作
     */
    public void start() throws Exception {

        // 加载解析server.xml、web.xml，初始化Servlet
        loadServlet();


        // 定义一个线程池
        int corePoolSize = 10;
        int maximumPoolSize = 50;
        long keepAliveTime = 100L;
        TimeUnit unit = TimeUnit.SECONDS;
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(50);
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();


        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                unit,
                workQueue,
                threadFactory,
                handler
        );

        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("=====>>>Minicat start on port：" + port);

        System.out.println("=========>>>>>>使用线程池进行多线程改造");
        /*
            多线程改造（使用线程池）
         */
        while (true) {

            Socket socket = serverSocket.accept();
            RequestProcessor requestProcessor = new RequestProcessor(socket, this.mapper);
            //requestProcessor.start();
            threadPoolExecutor.execute(requestProcessor);
        }


    }


    private Map<String, HttpServlet> servletMap = new HashMap<String, HttpServlet>();

    /**
     * 加载解析server.xml、web.xml，初始化Servlet
     */
    private void loadServlet() {
        //加载server.xml
        setServerParam();
        //获取每个项目的web.xml文件
        File file = new File(this.mapper.getHost().getAppBase());
        //处理每个子项目
        String[] list = file.list();
        for (String project : list) {
            try {
                String path = this.mapper.getHost().getAppBase() + "/" + project + "/web.xml";
                File demo = new File(path);
                InputStream resourceAsStream = new FileInputStream(demo);
                SAXReader saxReader = new SAXReader();
                Context context = new Context();
                context.setName(project);
                //设置项目
                mapper.getHost().getContextMap().put(project, context);
                Document document = saxReader.read(resourceAsStream);
                Element rootElement = document.getRootElement();

                List<Element> selectNodes = rootElement.selectNodes("//servlet");
                for (int i = 0; i < selectNodes.size(); i++) {
                    Element element = selectNodes.get(i);
                    // <servlet-name>lagou</servlet-name>
                    Element servletnameElement = (Element) element.selectSingleNode("servlet-name");
                    String servletName = servletnameElement.getStringValue();
                    // <servlet-class>server.LagouServlet</servlet-class>
                    Element servletclassElement = (Element) element.selectSingleNode("servlet-class");
                    String servletClass = servletclassElement.getStringValue();

                    // 根据servlet-name的值找到url-pattern
                    Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
                    // 添加项目目录 /demo1/lagou
                    String urlPattern = "/" + project + servletMapping.selectSingleNode("url-pattern").getStringValue();
                    Class<?> aClass = null;
                    if (WebConfig.getRootWebApp().equalsIgnoreCase(project)) {
                        //Mincat的自己的管理网站，不需要自定义类加载器
                        aClass = Class.forName(servletClass);
                    } else {
                        aClass = getaClass(servletClass, project);
                    }
                    //建立url拦截器和servlet的对应关系
                    Wrapper wrapper = new Wrapper();
                    wrapper.setName(urlPattern);
                    wrapper.setHttpServlet((HttpServlet) aClass.newInstance());
                    context.getWrapperMap().put(urlPattern,wrapper);
                }

            } catch (DocumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }


    }

    /**
     * 加载server.xml
     */
    private void setServerParam() {
        try {
            InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("server.xml");
            SAXReader saxReader = new SAXReader();
            Document read = saxReader.read(resourceAsStream);
            Document document = read.getDocument();
            Element rootElement = document.getRootElement();
            List<Element> list = rootElement.selectNodes("//Server");
            Element element = list.get(0);

            //获取端口
            Element connector = (Element) element.selectSingleNode("//Connector");
            String port = connector.attributeValue("port");
            if (StrUtil.isNotBlank(port)) {
                this.setPort(Integer.valueOf(port));
            }

            //获取host的本地目录
            Element hostElement = (Element)element.selectSingleNode("//Engine/Host");
            String name = hostElement.attributeValue("name");
            String appBase = hostElement.attributeValue("appBase");
            if (StrUtil.isNotBlank(name) && StrUtil.isNotBlank(appBase)) {
                Host host = new Host();
                host.setName(name);
                host.setAppBase(appBase);
                this.mapper.setHost(host);
            }else{
                throw new RuntimeException("Host的名字和本地目录不能为空！！！");
            }

        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }

    private Class<?> getaClass(String servletClass, String project) throws ClassNotFoundException {
        //将点替换成斜杠com.lagou.edu.DemoLagouServlet
        String fileName = servletClass.replaceAll("\\.", "/");

        //这个类class的路径
        String classPath = this.mapper.getHost().getAppBase()+  "/" + project + "/" + fileName + ".class";
        MyClassLoader myClassLoader = new MyClassLoader(classPath);

        //加载Log这个class文件
        Class<?> aClass = myClassLoader.findClass(servletClass);
        System.out.println("类加载器是:" + aClass.getClassLoader());
        return aClass;
    }


    /**
     * Minicat 的程序启动入口
     *
     * @param args
     */
    public static void main(String[] args) {
        Bootstrap bootstrap = new Bootstrap();
        try {
            // 启动Minicat
            bootstrap.start();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
